package com.derbysoft.synxisadapter.dswitch3.translator;

import com.derbysoft.core.utils.DateTimeUtils;
import com.derbysoft.dswitch.dto.common.KeyValue;
import com.derbysoft.dswitch.dto.common.TPAExtensionsDTO;
import com.derbysoft.dswitch.dto.hotel.common.DateRangeDTO;
import com.derbysoft.dswitch.dto.hotel.common.RateDTO;
import com.derbysoft.dswitch.dto.hotel.common.RoomRateDTO;
import com.derbysoft.dswitch.dto.hotel.reservation.ReservationRoomStayDTO;
import com.derbysoft.dswitch.remote.hotel.dto.ErrorDTO;
import com.derbysoft.dswitch.remote.hotel.dto.HotelReservationRequest;
import com.derbysoft.synxisadapter.commons.dto.synxis.*;
import com.derbysoft.synxisadapter.commons.exception.ChannelNotFoundException;
import com.derbysoft.synxisadapter.commons.exception.HotelNotFoundException;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.commons.model.Hotel;
import com.derbysoft.synxisadapter.commons.repository.ChannelRepository;
import com.derbysoft.synxisadapter.commons.repository.HotelRepository;
import com.derbysoft.synxisadapter.commons.utils.Constant;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

/**
 * User: jason
 * Date: 2012-11-21
 */
public abstract class AbstractTranslator extends ObjectFactory {

    protected static final String DEFAULT_ERROR_CODE = " ";
    public static final String PROVIDER_SOURCE = "From SynXis";
    public static final String ADAPTER_SOURCE = "From SynXis Adapter";
    protected static final String DEFAULT_ERROR_MSG = " ";
    protected static final String DATE_OF_RATE_PATTERN = "MM/dd/yyyy";

    @Autowired
    protected HotelRepository hotelRepository;

    @Autowired
    protected ChannelRepository channelRepository;

    protected Channel loadChannel(String channelCode) {
        Channel channel = channelRepository.findByCode(channelCode);

        if (channel == null) {
            throw new ChannelNotFoundException(channelCode);
        }
        return channel;
    }

    protected Hotel loadHotel(String hotelCode) {
        Hotel hotel = hotelRepository.findById(hotelCode);
        if (hotel == null) {
            throw new HotelNotFoundException(hotelCode);
        }
        return hotel;
    }

    protected POS translatePOS(String source) {
        POS pos = createPOS();
        pos.setSource(translateSource(loadChannel(source)));
        return pos;
    }

    protected ErrorDTO noAllotmentErrorDTO(HotelReservationRequest request) {
        ErrorDTO errorDTO = new ErrorDTO();
        errorDTO.setCode("NoAllotment");
        ReservationRoomStayDTO roomStayDTO = request.getHotelReservationRQ().getHotelReservation().getRoomStaysList().get(0);
        String ratePlan = roomStayDTO.getRatePlanCode();
        String roomType = roomStayDTO.getRoomTypeCode();
        int numberOfUnits = roomStayDTO.getNumberOfUnits();
        int adult = roomStayDTO.getAdultCount();
        int child = roomStayDTO.getChildCount();
        int los = DateTimeUtils.calcLOS(roomStayDTO.getStayDateRange().getCheckin(), roomStayDTO.getStayDateRange().getCheckout());
        errorDTO.setMessage(String.format("No allotment for ratePlanCode:[%s], roomTypeCode:[%s], " +
                "numberOfUnits:[%d], adult:[%d], child:[%d], los:[%d] !!", ratePlan, roomType, numberOfUnits, adult, child, los));
        errorDTO.setSource(ADAPTER_SOURCE);
        return errorDTO;
    }

    protected RoomRateDTO createDS3RoomRate(ArrayOfRateType arrayOfRateType, Channel channel) {
        RoomRateDTO roomRateDTO = new RoomRateDTO();
        ArrayList<RateDTO> ds3Rates = new ArrayList<RateDTO>();
        if (arrayOfRateType != null && CollectionUtils.isNotEmpty(arrayOfRateType.getRate())) {
            RateType rateType = arrayOfRateType.getRate().get(0);
            if (rateType.getTpaExtensions() != null && rateType.getTpaExtensions().getNightlyRate() != null) {
                for (NightlyRate nightlyRate : rateType.getTpaExtensions().getNightlyRate()) {
                    ds3Rates.add(createRateDTO(nightlyRate, channel));
                }
            }
        }

        roomRateDTO.setRatesList(combined(ds3Rates, channel));
        return roomRateDTO;
    }

    private List<RateDTO> combined(List<RateDTO> ds3Rates, Channel channel) {
        ArrayList<RateDTO> rateDTOs = new ArrayList<RateDTO>();
        for (RateDTO ds3Rate : ds3Rates) {
            int lastIndex = rateDTOs.size() - 1;
            if (rateDTOs.isEmpty() || rateDTOs.get(lastIndex <= 0 ? 0 : lastIndex) == null) {
                rateDTOs.add(ds3Rate);
            } else {
                RateDTO lastRateDTO = rateDTOs.get(lastIndex);
                if (sameAsLastRate(ds3Rate, lastRateDTO, channel)) {
                    String end = DateTimeUtils.formatDate(DateTimeUtils.addDays(DateTimeUtils.parse(lastRateDTO.getDateRange().getEnd()), 1));
                    lastRateDTO.getDateRange().setEnd(end);
                } else {
                    rateDTOs.add(ds3Rate);
                }
            }
        }
        return rateDTOs;
    }

    private boolean sameAsLastRate(RateDTO ds3Rate, RateDTO lastRateDTO, Channel channel) {
        if (channel.isInclusiveTaxRate()) {
            return ds3Rate.getAmountAfterTax().compareTo(lastRateDTO.getAmountAfterTax()) == 0;
        }
        return ds3Rate.getAmountBeforeTax().compareTo(lastRateDTO.getAmountBeforeTax()) == 0;
    }

    private RateDTO createRateDTO(NightlyRate nightlyRate, Channel channel) {
        RateDTO rateDTO = new RateDTO();
        String date = DateTimeUtils.formatDate(DateTimeUtils.parse(nightlyRate.getDate(), DATE_OF_RATE_PATTERN));
        rateDTO.setDateRange(new DateRangeDTO(date, date));
        if (channel.isInclusiveTaxRate()) {
            rateDTO.setAmountAfterTax(nightlyRate.getPriceWithTaxAndFee().doubleValue());
        } else {
            rateDTO.setAmountBeforeTax(nightlyRate.getPrice().doubleValue());
        }
        rateDTO.setDescription(translateDescription(nightlyRate));
        TPAExtensionsDTO tpaExtensions = new TPAExtensionsDTO();
        ArrayList<KeyValue> keyValues = new ArrayList<KeyValue>();
        String value = String.format("%s|%s|%s|%s", nightlyRate.getPrice(), nightlyRate.getTax(), nightlyRate.getFee(), nightlyRate.getPriceWithTaxAndFee());
        keyValues.add(new KeyValue(Constant.RATE_RULE, value));
        tpaExtensions.setElementsList(keyValues);
        rateDTO.setTpaExtensions(tpaExtensions);
        return rateDTO;
    }

    private String translateDescription(NightlyRate nightlyRate) {
        return String.format("Rate:%s Taxes:%s Fees:%s Total:%s",
                nightlyRate.getPrice(),
                nightlyRate.getTax(),
                nightlyRate.getFee(),
                nightlyRate.getPriceWithTaxAndFee());
    }

    private Source translateSource(Channel channel) {
        Source source = createSource();
        source.setRequestorId(translateRequeestorId(channel));
        return source;
    }

    private RequestorID translateRequeestorId(Channel channel) {
        RequestorID requestorID = createRequestorID();
        requestorID.setID(channel.getId());
        requestorID.setIDContext(Constant.CONTEXT_SYNXIS);
        requestorID.setCompanyName(translateCompanyName(channel));
        return requestorID;
    }

    private CompanyName translateCompanyName(Channel channel) {
        CompanyName companyName = createCompanyName();
        companyName.setCode(channel.getName());
        return companyName;
    }


    public void setHotelRepository(HotelRepository hotelRepository) {
        this.hotelRepository = hotelRepository;
    }

    public void setChannelRepository(ChannelRepository channelRepository) {
        this.channelRepository = channelRepository;
    }
}
